css: Do get_change directly on the tree without matching first
authorAlexander Larsson <alexl@redhat.com>
Mon, 10 Dec 2012 10:27:43 +0000 (11:27 +0100)
committerAlexander Larsson <alexl@redhat.com>
Mon, 10 Dec 2012 11:11:02 +0000 (12:11 +0100)
Rather than first collecting matches and then getting the change
for them we do the change collection directly on the tree. This
is about twice as fast.

gtk/gtkcssprovider.c
gtk/gtkcssselector.c
gtk/gtkcssselectorprivate.h
gtk/gtkcsstypesprivate.h

index a8e13edfab98c45563f2123603b1cd5411091740..751266847464ff2e9d495af992c5b203d60c7e8a 100644 (file)
@@ -1273,12 +1273,6 @@ gtk_css_ruleset_add (GtkCssRuleset       *ruleset,
     ruleset->styles[i].section = NULL;
 }
 
-static GtkCssChange
-gtk_css_ruleset_get_change (GtkCssRuleset *ruleset)
-{
-  return _gtk_css_selector_tree_match_get_change (ruleset->selector_match);
-}
-
 static void
 gtk_css_scanner_destroy (GtkCssScanner *scanner)
 {
@@ -1441,6 +1435,49 @@ verify_tree_match_results (GtkCssProvider *provider,
 #endif
 }
 
+static void
+verify_tree_get_change_results (GtkCssProvider *provider,
+                               const GtkCssMatcher *matcher,
+                               GtkCssChange change)
+{
+#ifdef VERIFY_TREE
+  {
+    GtkCssChange verify_change = 0;
+    GPtrArray *tree_rules;
+    int i;
+
+    tree_rules = _gtk_css_selector_tree_match_all (provider->priv->tree, matcher);
+    verify_tree_match_results (provider, matcher, tree_rules);
+
+    for (i = tree_rules->len - 1; i >= 0; i--)
+      {
+       GtkCssRuleset *ruleset;
+
+       ruleset = tree_rules->pdata[i];
+
+       verify_change |= _gtk_css_selector_tree_match_get_change (ruleset->selector_match);
+      }
+
+    if (change != verify_change)
+      {
+       GString *s;
+
+       s = g_string_new ("");
+       g_string_append_printf (s, "expected change 0x%x, but it was 0x%x", verify_change, change);
+       if ((change & ~verify_change) != 0)
+         g_string_append_printf (s, ", unexpectedly set: 0x%x", change & ~verify_change);
+       if ((~change & verify_change) != 0)
+         g_string_append_printf (s, ", unexpectedly no set: 0x%x",  ~change & verify_change);
+       g_warning (s->str);
+       g_string_free (s, TRUE);
+      }
+
+    g_ptr_array_free (tree_rules, TRUE);
+  }
+#endif
+}
+
+
 static gboolean
 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
                                      GtkWidgetPath    *path,
@@ -1585,29 +1622,14 @@ gtk_css_style_provider_get_change (GtkStyleProviderPrivate *provider,
 {
   GtkCssProvider *css_provider;
   GtkCssProviderPrivate *priv;
-  GtkCssChange change = 0;
-  GPtrArray *tree_rules;
-  int i;
+  GtkCssChange change;
 
   css_provider = GTK_CSS_PROVIDER (provider);
   priv = css_provider->priv;
 
-  tree_rules = _gtk_css_selector_tree_match_all (priv->tree, matcher);
-  verify_tree_match_results (css_provider, matcher, tree_rules);
-
-  for (i = tree_rules->len - 1; i >= 0; i--)
-    {
-      GtkCssRuleset *ruleset;
-
-      ruleset = tree_rules->pdata[i];
+  change = _gtk_css_selector_tree_get_change_all (priv->tree, matcher);
 
-      if (ruleset->styles == NULL)
-        continue;
-
-      change |= gtk_css_ruleset_get_change (ruleset);
-    }
-
-  g_ptr_array_free (tree_rules, TRUE);
+  verify_tree_get_change_results (css_provider, matcher, change);
 
   return change;
 }
index 72f1eadd920496db5b79058041d0d4876240c3b7..a6b6947a9ba09aad5539252cd433ec8dedbeeefa 100644 (file)
 #include "gtkcssprovider.h"
 #include "gtkstylecontextprivate.h"
 
+/* When checking for changes via the tree we need to know if a rule further
+   down the tree matched, because if so we need to add "our bit" to the
+   Change. For instance in a a match like *.class:active we'll
+   get a tree that first checks :active, if that matches we continue down
+   to the tree, and if we get a match we add CHANGE_CLASS. However, the
+   end of the tree where we have a match is an ANY which doesn't actually
+   modify the change, so we don't know if we have a match or not. We fix
+   this by setting GTK_CSS_CHANGE_GOT_MATCH which lets us guarantee
+   that change != 0 on any match. */
+#define GTK_CSS_CHANGE_GOT_MATCH GTK_CSS_CHANGE_RESERVED_BIT
+
 typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
 
 struct _GtkCssSelectorClass {
@@ -39,6 +50,8 @@ struct _GtkCssSelectorClass {
                                     GHashTable                  *res);
   GtkCssChange      (* get_change)  (const GtkCssSelector       *selector,
                                     GtkCssChange                previous_change);
+  GtkCssChange      (* tree_get_change)  (const GtkCssSelectorTree *tree,
+                                         const GtkCssMatcher      *matcher);
   int               (* compare_one) (const GtkCssSelector       *a,
                                     const GtkCssSelector       *b);
 
@@ -117,6 +130,16 @@ gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
   tree->selector.class->tree_match (tree, matcher, res);
 }
 
+static GtkCssChange
+gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
+                                 const GtkCssMatcher      *matcher)
+{
+  if (tree == NULL)
+    return 0;
+
+  return tree->selector.class->tree_get_change (tree, matcher);
+}
+
 static gboolean
 gtk_css_selector_match (const GtkCssSelector *selector,
                         const GtkCssMatcher  *matcher)
@@ -172,6 +195,21 @@ gtk_css_selector_tree_get_sibling (const GtkCssSelectorTree *tree)
   return gtk_css_selector_tree_at_offset (tree, tree->sibling_offset);
 }
 
+static GtkCssChange
+gtk_css_selector_tree_get_previous_change (const GtkCssSelectorTree *tree,
+                                          const GtkCssMatcher      *matcher)
+{
+  GtkCssChange previous_change = 0;
+  const GtkCssSelectorTree *prev;
+
+  for (prev = gtk_css_selector_tree_get_previous (tree);
+       prev != NULL;
+       prev = gtk_css_selector_tree_get_sibling (prev))
+    previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+
+  return previous_change;
+}
+
 /* DESCENDANT */
 
 static void
@@ -222,6 +260,33 @@ gtk_css_selector_descendant_tree_match (const GtkCssSelectorTree *tree,
     }
 }
 
+static GtkCssChange
+gtk_css_selector_descendant_tree_get_change (const GtkCssSelectorTree *tree,
+                                            const GtkCssMatcher  *matcher)
+{
+  GtkCssMatcher ancestor;
+  GtkCssChange change, previous_change;
+
+  change = 0;
+  previous_change = 0;
+  while (_gtk_css_matcher_get_parent (&ancestor, matcher))
+    {
+      matcher = &ancestor;
+
+      previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+      /* any matchers are dangerous here, as we may loop forever, but
+        we can terminate now as all possible matches have already been added */
+      if (_gtk_css_matcher_matches_any (matcher))
+       break;
+    }
+
+  if (previous_change != 0)
+    change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
 static int
 gtk_css_selector_descendant_compare_one (const GtkCssSelector *a,
                                         const GtkCssSelector *b)
@@ -241,6 +306,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
   gtk_css_selector_descendant_match,
   gtk_css_selector_descendant_tree_match,
   gtk_css_selector_descendant_get_change,
+  gtk_css_selector_descendant_tree_get_change,
   gtk_css_selector_descendant_compare_one,
   FALSE, FALSE, FALSE, FALSE, FALSE
 };
@@ -283,6 +349,27 @@ gtk_css_selector_child_tree_match (const GtkCssSelectorTree *tree,
     gtk_css_selector_tree_match (prev, &parent, res);
 }
 
+
+static GtkCssChange
+gtk_css_selector_child_tree_get_change (const GtkCssSelectorTree *tree,
+                                       const GtkCssMatcher  *matcher)
+{
+  GtkCssMatcher parent;
+  GtkCssChange change, previous_change;
+
+  if (!_gtk_css_matcher_get_parent (&parent, matcher))
+    return 0;
+
+  change = 0;
+
+  previous_change = gtk_css_selector_tree_get_previous_change (tree, &parent);
+
+  if (previous_change != 0)
+    change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
 static GtkCssChange
 gtk_css_selector_child_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
 {
@@ -302,6 +389,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
   gtk_css_selector_child_match,
   gtk_css_selector_child_tree_match,
   gtk_css_selector_child_get_change,
+  gtk_css_selector_child_tree_get_change,
   gtk_css_selector_child_compare_one,
   FALSE, FALSE, FALSE, FALSE, FALSE
 };
@@ -356,6 +444,34 @@ gtk_css_selector_sibling_tree_match (const GtkCssSelectorTree *tree,
     }
 }
 
+static GtkCssChange
+gtk_css_selector_sibling_tree_get_change (const GtkCssSelectorTree *tree,
+                                         const GtkCssMatcher  *matcher)
+{
+  GtkCssMatcher previous;
+  GtkCssChange change, previous_change;
+
+  change = 0;
+
+  previous_change = 0;
+  while (_gtk_css_matcher_get_previous (&previous, matcher))
+    {
+      matcher = &previous;
+
+      previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+      /* any matchers are dangerous here, as we may loop forever, but
+        we can terminate now as all possible matches have already been added */
+      if (_gtk_css_matcher_matches_any (matcher))
+       break;
+    }
+
+  if (previous_change != 0)
+    change |= _gtk_css_change_for_sibling (previous_change) |  GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
 static GtkCssChange
 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
 {
@@ -376,6 +492,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
   gtk_css_selector_sibling_match,
   gtk_css_selector_sibling_tree_match,
   gtk_css_selector_sibling_get_change,
+  gtk_css_selector_sibling_tree_get_change,
   gtk_css_selector_sibling_compare_one,
   FALSE, FALSE, FALSE, FALSE, FALSE
 };
@@ -420,6 +537,26 @@ gtk_css_selector_adjacent_tree_match (const GtkCssSelectorTree *tree,
     gtk_css_selector_tree_match (prev, matcher, res);
 }
 
+static GtkCssChange
+gtk_css_selector_adjacent_tree_get_change (const GtkCssSelectorTree *tree,
+                                          const GtkCssMatcher  *matcher)
+{
+  GtkCssMatcher previous;
+  GtkCssChange change, previous_change;
+
+  if (!_gtk_css_matcher_get_previous (&previous, matcher))
+    return 0;
+
+  change = 0;
+
+  previous_change = gtk_css_selector_tree_get_previous_change (tree, &previous);
+
+  if (previous_change != 0)
+    change |= _gtk_css_change_for_sibling (previous_change) |  GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
 static GtkCssChange
 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
 {
@@ -439,6 +576,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
   gtk_css_selector_adjacent_match,
   gtk_css_selector_adjacent_tree_match,
   gtk_css_selector_adjacent_get_change,
+  gtk_css_selector_adjacent_tree_get_change,
   gtk_css_selector_adjacent_compare_one,
   FALSE, FALSE, FALSE, FALSE, FALSE
 };
@@ -495,6 +633,37 @@ gtk_css_selector_any_tree_match (const GtkCssSelectorTree *tree,
     }
 }
 
+static GtkCssChange
+gtk_css_selector_any_tree_get_change (const GtkCssSelectorTree *tree,
+                                     const GtkCssMatcher  *matcher)
+{
+  const GtkCssSelectorTree *prev;
+  GtkCssChange change, previous_change;
+
+  change = 0;
+
+  if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+    change |= GTK_CSS_CHANGE_GOT_MATCH;
+
+  previous_change = 0;
+  for (prev = gtk_css_selector_tree_get_previous (tree);
+       prev != NULL;
+       prev = gtk_css_selector_tree_get_sibling (prev))
+    {
+      if (prev->selector.class == &GTK_CSS_SELECTOR_DESCENDANT &&
+         _gtk_css_matcher_has_regions (matcher))
+       previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
+
+      previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+    }
+
+  if (previous_change != 0)
+    change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
+
 static GtkCssChange
 gtk_css_selector_any_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
 {
@@ -514,6 +683,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
   gtk_css_selector_any_match,
   gtk_css_selector_any_tree_match,
   gtk_css_selector_any_get_change,
+  gtk_css_selector_any_tree_get_change,
   gtk_css_selector_any_compare_one,
   FALSE, FALSE, FALSE, TRUE, TRUE
 };
@@ -555,6 +725,28 @@ gtk_css_selector_name_tree_match (const GtkCssSelectorTree *tree,
     gtk_css_selector_tree_match (prev, matcher, res);
 }
 
+static GtkCssChange
+gtk_css_selector_name_tree_get_change (const GtkCssSelectorTree *tree,
+                                      const GtkCssMatcher  *matcher)
+{
+  GtkCssChange change, previous_change;
+
+  if (!_gtk_css_matcher_has_name (matcher, tree->selector.data))
+    return 0;
+
+  change = 0;
+
+  if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+    change |= GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
+
+  previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+  if (previous_change)
+    change |= previous_change | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
 
 static GtkCssChange
 gtk_css_selector_name_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
@@ -575,6 +767,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
   gtk_css_selector_name_match,
   gtk_css_selector_name_tree_match,
   gtk_css_selector_name_get_change,
+  gtk_css_selector_name_tree_get_change,
   gtk_css_selector_name_compare_one,
   FALSE, FALSE, TRUE, TRUE, FALSE
 };
@@ -633,6 +826,42 @@ gtk_css_selector_region_tree_match (const GtkCssSelectorTree *tree,
     }
 }
 
+static GtkCssChange
+gtk_css_selector_region_tree_get_change (const GtkCssSelectorTree *tree,
+                                        const GtkCssMatcher  *matcher)
+{
+  const GtkCssSelectorTree *prev;
+  GtkCssChange change, previous_change;
+
+  if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
+    return 0;
+
+  change = 0;
+
+  if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+    change |= GTK_CSS_CHANGE_REGION | GTK_CSS_CHANGE_GOT_MATCH;
+
+  previous_change = 0;
+  for (prev = gtk_css_selector_tree_get_previous (tree);
+       prev != NULL;
+       prev = gtk_css_selector_tree_get_sibling (prev))
+    {
+      if (prev->selector.class == &GTK_CSS_SELECTOR_DESCENDANT)
+       previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
+
+      previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+    }
+
+  if (previous_change != 0)
+    {
+      previous_change |= GTK_CSS_CHANGE_REGION;
+      previous_change |= _gtk_css_change_for_child (previous_change);
+      change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
+    }
+
+  return change;
+}
+
 static GtkCssChange
 gtk_css_selector_region_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
 {
@@ -658,6 +887,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
   gtk_css_selector_region_match,
   gtk_css_selector_region_tree_match,
   gtk_css_selector_region_get_change,
+  gtk_css_selector_region_tree_get_change,
   gtk_css_selector_region_compare_one,
   FALSE, FALSE, TRUE, TRUE, TRUE
 };
@@ -700,6 +930,28 @@ gtk_css_selector_class_tree_match (const GtkCssSelectorTree *tree,
     gtk_css_selector_tree_match (prev, matcher, res);
 }
 
+static GtkCssChange
+gtk_css_selector_class_tree_get_change (const GtkCssSelectorTree *tree,
+                                       const GtkCssMatcher  *matcher)
+{
+  GtkCssChange change, previous_change;
+
+  if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
+    return 0;
+
+  change = 0;
+
+  if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+    change |= GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
+
+  previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+  if (previous_change != 0)
+    change |= previous_change | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
 static GtkCssChange
 gtk_css_selector_class_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
 {
@@ -720,6 +972,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
   gtk_css_selector_class_match,
   gtk_css_selector_class_tree_match,
   gtk_css_selector_class_get_change,
+  gtk_css_selector_class_tree_get_change,
   gtk_css_selector_class_compare_one,
   FALSE, TRUE, FALSE, TRUE, FALSE
 };
@@ -762,6 +1015,28 @@ gtk_css_selector_id_tree_match (const GtkCssSelectorTree *tree,
     gtk_css_selector_tree_match (prev, matcher, res);
 }
 
+static GtkCssChange
+gtk_css_selector_id_tree_get_change (const GtkCssSelectorTree *tree,
+                                    const GtkCssMatcher  *matcher)
+{
+  GtkCssChange change, previous_change;
+
+  if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
+    return 0;
+
+  change = 0;
+
+  if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+    change |= GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_GOT_MATCH;
+
+  previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+  if (previous_change != 0)
+    change |= previous_change | GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
 static GtkCssChange
 gtk_css_selector_id_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
 {
@@ -782,6 +1057,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
   gtk_css_selector_id_match,
   gtk_css_selector_id_tree_match,
   gtk_css_selector_id_get_change,
+  gtk_css_selector_id_tree_get_change,
   gtk_css_selector_id_compare_one,
   TRUE, FALSE, FALSE, TRUE, FALSE
 };
@@ -849,6 +1125,28 @@ gtk_css_selector_pseudoclass_state_tree_match (const GtkCssSelectorTree *tree,
     gtk_css_selector_tree_match (prev, matcher, res);
 }
 
+static GtkCssChange
+gtk_css_selector_pseudoclass_state_tree_get_change (const GtkCssSelectorTree *tree,
+                                                   const GtkCssMatcher  *matcher)
+{
+  GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
+  GtkCssChange change, previous_change;
+
+  if ((_gtk_css_matcher_get_state (matcher) & state) != state)
+    return 0;
+
+  change = 0;
+
+  if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+    change |= GTK_CSS_CHANGE_STATE | GTK_CSS_CHANGE_GOT_MATCH;
+
+  previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+  if (previous_change != 0)
+    change |= previous_change | GTK_CSS_CHANGE_STATE | GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
 
 static GtkCssChange
 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
@@ -869,6 +1167,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
   gtk_css_selector_pseudoclass_state_match,
   gtk_css_selector_pseudoclass_state_tree_match,
   gtk_css_selector_pseudoclass_state_get_change,
+  gtk_css_selector_pseudoclass_state_tree_get_change,
   gtk_css_selector_pseudoclass_state_compare_one,
   FALSE, TRUE, FALSE, TRUE, FALSE
 };
@@ -1176,6 +1475,77 @@ gtk_css_selector_pseudoclass_position_tree_match (const GtkCssSelectorTree *tree
     }
 }
 
+static GtkCssChange
+gtk_css_selector_pseudoclass_position_tree_get_change_for_region (const GtkCssSelectorTree *tree,
+                                                                 const GtkCssSelectorTree *prev,
+                                                                 const GtkCssMatcher  *matcher)
+{
+  const GtkCssSelectorTree *prev2;
+  GtkRegionFlags selector_flags;
+  GtkCssChange change, previous_change;
+
+  if (!get_selector_flags_for_position_region_match (&tree->selector, &selector_flags))
+      return 0;
+
+  if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
+    return 0;
+
+  change = 0;
+  if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+    change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
+
+  previous_change = 0;
+  for (prev2 = gtk_css_selector_tree_get_previous (prev);
+       prev2 != NULL;
+       prev2 = gtk_css_selector_tree_get_sibling (prev2))
+    {
+      if (prev2->selector.class == &GTK_CSS_SELECTOR_DESCENDANT)
+       previous_change |= gtk_css_selector_tree_get_change (gtk_css_selector_tree_get_previous (prev2), matcher);
+      previous_change |= gtk_css_selector_tree_get_change (prev2, matcher);
+    }
+
+  if (previous_change != 0)
+    change |= previous_change | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
+static GtkCssChange
+gtk_css_selector_pseudoclass_position_tree_get_change (const GtkCssSelectorTree *tree,
+                                                      const GtkCssMatcher  *matcher)
+{
+  const GtkCssSelectorTree *prev;
+  GtkCssChange change, previous_change;
+
+  change = 0;
+
+  for (prev = gtk_css_selector_tree_get_previous (tree);
+       prev != NULL;
+       prev = gtk_css_selector_tree_get_sibling (prev))
+    {
+      if (prev->selector.class == &GTK_CSS_SELECTOR_REGION)
+       change |= gtk_css_selector_pseudoclass_position_tree_get_change_for_region (tree, prev, matcher);
+    }
+
+  if (!get_position_match (&tree->selector, matcher))
+    return change;
+
+  if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+    change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
+
+  previous_change = 0;
+  for (prev = gtk_css_selector_tree_get_previous (tree); prev != NULL; prev = gtk_css_selector_tree_get_sibling (prev))
+    {
+      if (prev->selector.class != &GTK_CSS_SELECTOR_REGION)
+       previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+    }
+
+  if (previous_change != 0)
+    change |= previous_change | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
+
+  return change;
+}
+
 static GtkCssChange
 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
 {
@@ -1195,6 +1565,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
   gtk_css_selector_pseudoclass_position_match,
   gtk_css_selector_pseudoclass_position_tree_match,
   gtk_css_selector_pseudoclass_position_get_change,
+  gtk_css_selector_pseudoclass_position_tree_get_change,
   gtk_css_selector_pseudoclass_position_compare_one,
   FALSE, TRUE, FALSE, TRUE, TRUE
 };
@@ -1765,6 +2136,22 @@ _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
   return array;
 }
 
+GtkCssChange
+_gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
+                                      const GtkCssMatcher *matcher)
+{
+  GtkCssChange change;
+
+  change = 0;
+
+  for (; tree != NULL;
+       tree = gtk_css_selector_tree_get_sibling (tree))
+    change |= gtk_css_selector_tree_get_change (tree, matcher);
+
+  /* Never return reserved bit set */
+  return change & ~GTK_CSS_CHANGE_RESERVED_BIT;
+}
+
 #ifdef PRINT_TREE
 static void
 _gtk_css_selector_tree_print (GtkCssSelectorTree *tree, GString *str, char *prefix)
index cc7ec4b48ffc6d9417ce5df881087259d3e0b626..17e3618d5f71292a2a2a357c799d568856e21541 100644 (file)
@@ -42,6 +42,8 @@ int               _gtk_css_selector_compare         (const GtkCssSelector   *a,
 void         _gtk_css_selector_tree_free             (GtkCssSelectorTree       *tree);
 GPtrArray *  _gtk_css_selector_tree_match_all        (const GtkCssSelectorTree *tree,
                                                      const GtkCssMatcher      *matcher);
+GtkCssChange _gtk_css_selector_tree_get_change_all   (const GtkCssSelectorTree *tree,
+                                                     const GtkCssMatcher *matcher);
 void         _gtk_css_selector_tree_match_print      (const GtkCssSelectorTree *tree,
                                                      GString                  *str);
 GtkCssChange _gtk_css_selector_tree_match_get_change (const GtkCssSelectorTree *tree);
index d792c3c03dc9a2b12a8a2f76f11e049112401e4d..9c1f92ba2bf6f9977c98130030f0c3ac44f0ffa3 100644 (file)
@@ -50,7 +50,9 @@ typedef enum { /*< skip >*/
   /* add more */
   GTK_CSS_CHANGE_SOURCE                   = (1 << 16),
   GTK_CSS_CHANGE_ANIMATE                  = (1 << 17),
-  GTK_CSS_CHANGE_FORCE_INVALIDATE         = (1 << 18)
+  GTK_CSS_CHANGE_FORCE_INVALIDATE         = (1 << 18),
+
+  GTK_CSS_CHANGE_RESERVED_BIT             = (1 << 31) /* Used internally in gtkcssselector.c */
 } GtkCssChange;
 
 #define GTK_CSS_CHANGE_ANY ((1 << 19) - 1)